package com.coocaa.typhoon.api.third.volcengine.util;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author bijiahao
 * @date : 2025/9/25 16:32
 * @description
 */
@Slf4j
public class OkhttpSign {
    private static final BitSet URLENCODER = new BitSet(256);

    private static final String CONST_ENCODE = "0123456789ABCDEF";
    public static final Charset UTF_8 = StandardCharsets.UTF_8;

    private final String region;
    private final String service;
    private final String schema;
    private final String host;
    private final String path;
    private final String ak;
    private final String sk;
    private OkHttpClient client;

    static {
        int i;
        for (i = 97; i <= 122; ++i) {
            URLENCODER.set(i);
        }

        for (i = 65; i <= 90; ++i) {
            URLENCODER.set(i);
        }

        for (i = 48; i <= 57; ++i) {
            URLENCODER.set(i);
        }
        URLENCODER.set('-');
        URLENCODER.set('_');
        URLENCODER.set('.');
        URLENCODER.set('~');
    }

    public OkhttpSign(String region, String path, String ak, String sk, OkHttpClient client) {
        this.region = region;
        this.service = "vikingdb";
        ;
        this.host = "api-vikingdb.vikingdb.cn-beijing.volces.com";
        this.schema = "https";
        this.path = path;
        this.ak = ak;
        this.sk = sk;
        this.client = client;
    }

    public String doRequest(String method, Map<String, String> queryList, String bodyStr) {
        try {
            byte[] body = null;
            if (StringUtils.isBlank(bodyStr)) {
                body = new byte[0];
            } else {
                body = bodyStr.getBytes();
            }
            Date date = new Date();
            String xContentSha256 = hashSHA256(body);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
            String xDate = sdf.format(date);
            String shortXDate = xDate.substring(0, 8);
            String contentType = "application/x-www-form-urlencoded";
            String signHeader = "host;x-date;x-content-sha256;content-type";
            SortedMap<String, String> realQueryList = new TreeMap<>(queryList);
            StringBuilder querySB = new StringBuilder();
            for (String key : realQueryList.keySet()) {
                querySB.append(signStringEncoder(key)).append("=").append(signStringEncoder(realQueryList.get(key))).append("&");
            }
            if (querySB.length() > 0) {
                querySB.deleteCharAt(querySB.length() - 1);
            }
            String canonicalStringBuilder = method + "\n" + path + "\n" + querySB + "\n" +
                    "host:" + host + "\n" +
                    "x-date:" + xDate + "\n" +
                    "x-content-sha256:" + xContentSha256 + "\n" +
                    "content-type:" + contentType + "\n" +
                    "\n" +
                    signHeader + "\n" +
                    xContentSha256;
            String hashcanonicalString = hashSHA256(canonicalStringBuilder.getBytes());
            String credentialScope = shortXDate + "/" + region + "/" + service + "/request";
            String signString = "HMAC-SHA256" + "\n" + xDate + "\n" + credentialScope + "\n" + hashcanonicalString;

            byte[] signKey = genSigningSecretKeyV4(sk, shortXDate, region, service);
            String signature = Hex.encodeHexString(hmacSHA256(signKey, signString));


            // 1. 构造 URL（含 query）
            HttpUrl httpUrl = HttpUrl.parse(schema + "://" + host + path)
                    .newBuilder()
                    .encodedQuery(querySB.toString())
                    .build();
            // 2. 构造 Request
            Request.Builder reqBuilder = new Request.Builder()
                    .url(httpUrl)
                    .addHeader("Host", host)
                    .addHeader("X-Date", xDate)
                    .addHeader("X-Content-Sha256", xContentSha256)
                    .addHeader("Content-Type", contentType)
                    .addHeader("Authorization", "HMAC-SHA256" +
                            " Credential=" + ak + "/" + credentialScope +
                            ", SignedHeaders=" + signHeader +
                            ", Signature=" + signature);

            if (!"GET".equalsIgnoreCase(method)) {
                reqBuilder.method(method, RequestBody.create(body, MediaType.parse(contentType)));
            } else {
                reqBuilder.method(method, null);
            }
            // 3. 发起调用
            Request request = reqBuilder.build();
            long startTime = System.currentTimeMillis();
            try (Response response = client.newCall(request).execute()) {
                String result = response.body() != null ? response.body().string() : "";
                long end = System.currentTimeMillis();
                log.info("request: {} , response: {} , cost: {} ms", bodyStr, result, end - startTime);
                return result;
            } catch (Exception e) {
                log.error("获取火山引擎接口结果异常: " + JSON.toJSONString(bodyStr), e);
                return null;
            }
        } catch (Exception e) {
            log.error("调用火山引擎接口 error", e);
            return null;
        }
    }

    private String signStringEncoder(String source) {
        if (source == null) {
            return null;
        }
        StringBuilder buf = new StringBuilder(source.length());
        ByteBuffer bb = UTF_8.encode(source);
        while (bb.hasRemaining()) {
            int b = bb.get() & 255;
            if (URLENCODER.get(b)) {
                buf.append((char) b);
            } else if (b == 32) {
                buf.append("%20");
            } else {
                buf.append("%");
                char hex1 = CONST_ENCODE.charAt(b >> 4);
                char hex2 = CONST_ENCODE.charAt(b & 15);
                buf.append(hex1);
                buf.append(hex2);
            }
        }

        return buf.toString();
    }

    public static String hashSHA256(byte[] content) throws Exception {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");

            return Hex.encodeHexString(md.digest(content));
        } catch (Exception e) {
            throw new Exception(
                    "Unable to compute hash while signing request: "
                            + e.getMessage(), e);
        }
    }

    public static byte[] hmacSHA256(byte[] key, String content) throws Exception {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key, "HmacSHA256"));
            return mac.doFinal(content.getBytes());
        } catch (Exception e) {
            throw new Exception(
                    "Unable to calculate a request signature: "
                            + e.getMessage(), e);
        }
    }

    private byte[] genSigningSecretKeyV4(String secretKey, String date, String region, String service) throws Exception {
        byte[] kDate = hmacSHA256((secretKey).getBytes(), date);
        byte[] kRegion = hmacSHA256(kDate, region);
        byte[] kService = hmacSHA256(kRegion, service);
        return hmacSHA256(kService, "request");
    }
}
